//
// Filter
//

#ifndef FILTER_H
#define FILTER_H

#include "structures.h"

// Min/Max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))

// Kernels for edge and blur filters
float Edge[] =
{
	 0, -1,  0,
	-1,  4, -1,
	 0, -1,  0
};

float Blur[] =
{
	0.0, 0.2, 0.0,
	0.2, 0.2, 0.2,
	0.0, 0.2, 0.0
};

const int filterWidth = 3;
const int filterHeight = 3;

// Filters row and writes the result to destRow,
// uses preRow and postRow for fetching neighbouring pixels. 
// Parameter y is the absolute row number with respect to the top of the image.
void filterRow(Row* preRow, Row* row, Row* postRow, Row* destRow, int y) {

	Row* rows[3] = {preRow, row, postRow};

	int x;
	for (x = 0; x < IMAGE_WIDTH; x++) {
	
		// Edge filter
		float edge = 0.0f;

		int filterX;
		for (filterX = -1; filterX <= +1; filterX++) {
			int filterY;
			for (filterY = 0; filterY < 3; filterY++) {
			
				Row* filterRow = rows[filterY];

				Pixel pixel = filterRow->pixels[(x + filterX + IMAGE_WIDTH) % IMAGE_WIDTH];

				float kernel = Edge[(filterX + 1) + filterY * 3];

				int pixelSum = pixel.R + pixel.G + pixel.B;

				edge += pixelSum * kernel;
			}
		}

		// Check if pixel is above edge treshold
		const float edgeTreshold = 61.2f; // 0.08f * 255 * 3

		// Filter
		float red = 0.0f;
		float green = 0.0f;
		float blue = 0.0f;

		float distanceToCenter = (sqrtf(powf((x - IMAGE_WIDTH / 2), 2) + powf((y - IMAGE_HEIGHT / 2), 2)));

		float val = distanceToCenter / 255;
		float valr = (sqrtf(powf((x - IMAGE_WIDTH/2),2) + powf((y - IMAGE_HEIGHT/2),2))) / 255;
		float valg = (sqrtf(powf((x - 50 - IMAGE_WIDTH/2),2) + powf((y + 75 - IMAGE_HEIGHT/2),2))) / 255;
		float valb = (sqrtf(powf((x + 50 - IMAGE_WIDTH/2),2) + powf((y + 75 - IMAGE_HEIGHT/2),2))) / 255;
		
		if (edge <= edgeTreshold) {

			// Blur filter
			int filterX;
			for (filterX = -1; filterX <= +1; filterX++) {
	
				int filterY;
				for (filterY = 0; filterY < 3; filterY++) {
			
					Row* filterRow = rows[filterY];

					Pixel pixel = filterRow->pixels[(x + filterX + IMAGE_WIDTH) % IMAGE_WIDTH];

					float kernel = Blur[(filterX + 1) + filterY * 3];

					red += pixel.R * kernel;
					green += pixel.G * kernel;
					blue += pixel.B * kernel;
				}
			}

			// Linear interpolate between source pixel and blurred pixel
			red   = row->pixels[x].R*(1-val) + val*red;
			green = row->pixels[x].G*(1-val) + val*green;
			blue  = row->pixels[x].B*(1-val) + val*blue;

		} else {
			red   = row->pixels[x].R;
			green = row->pixels[x].G;
			blue  = row->pixels[x].B;
		}

		// Scale and clamp to [0, 255]
		unsigned char R = (unsigned char) min(max((int)((1.3f + valr * 0.3f) * red + 0), 0), 255);
		unsigned char G = (unsigned char) min(max((int)((1.0f + valg * 0.3f) * green + 0), 0), 255);
		unsigned char B = (unsigned char) min(max((int)((0.7f + valb * 0.3f) * blue + 0), 0), 255);

		// Scale based on distance to center,
		// write the result to destRow
		float scaleDistance = (355 - distanceToCenter) / 255;

		destRow->pixels[x].R = (unsigned char) min(max((int)(scaleDistance*R), 0), 255);
		destRow->pixels[x].G = (unsigned char) min(max((int)(scaleDistance*G), 0), 255);
		destRow->pixels[x].B = (unsigned char) min(max((int)(scaleDistance*B), 0), 255);
	}
}

#endif
